#pragma once

#include <dinfra/init_fin.h>
#include <dinfra/mplf/tstack.h>

/* wmobj_t* */

/* 按cacheline对齐的全局共享永久对象的分配(锁, 锁节点, )等，统一分配的目的是放在同一个页里面减少共享对象访问时的tlbmiss */
/* 浮标式， 前面用于分配对齐对象(sptr), 后面用于分配不定长对象 */
typedef struct wmobj{
	struct{
		tsnode_t node;
	}ALIGN(HDEF_CACHE_LINE_BYTES);
}wmobj_t;

typedef struct wmsp{
	/* free索引 */
	void			*sptr;
	void			*eptr;

	tstack_t		flist;
}wmsp_t;

SAI wmobj_t *wmsp_alloc_head(wmsp_t *sl, const size_t obj_size){
	wmobj_t *ret = NULL;
	if (sl->flist.head){
		tsnode_t *poped = tstack_pop(&sl->flist);
		if (poped){
			ret = tstack_entry(poped, wmobj_t, node);
			return ret;
		}
	}
	void *eptr = atm_ld(&sl->eptr);
	void *sptr = atm_ld(&sl->sptr);
	if ((paddr_t)sptr + obj_size <= (paddr_t)eptr){
		void *tmp_ptr = faa(&sl->sptr, obj_size);
		/* 加完后依然有效则可以 */
		if ((paddr_t)tmp_ptr + obj_size <= (paddr_t)atm_ld(&sl->eptr)){
			ret = (wmobj_t *)tmp_ptr;
			// dbg_printf("[faa get]%p, %ld\n", ret, idx);
			return ret;
		}
	}
	return ret;
}

/* 这样可以在一个巨页中 */
/* 从尾部申请 */
SAI wmobj_t *wmsp_alloc_tail(wmsp_t *sl, size_t size){
	wmobj_t *ret = NULL;
	if ((paddr_t)sl->sptr + size <= (paddr_t)sl->eptr){
		void *tmp_ptr = (void *)((paddr_t)faa(&sl->eptr, (0 - size)) - size);
		/* 加完后依然有效则可以 */
		if ((paddr_t)atm_ld(&sl->sptr) <= (paddr_t)tmp_ptr){
			ret = (wmobj_t *)tmp_ptr;
			// dbg_printf("[faa get]%p, %ld\n", ret, idx);
			return ret;
		}
	}
	return ret;
}

SAI void wmsp_free_batch(wmsp_t *sl, wmobj_t *hd, wmobj_t *tl){
	__tstack_push_batch(&sl->flist, &hd->node, &tl->node);
}

SAI void wmsp_free(wmsp_t *sl, wmobj_t *hd){
	wmsp_free_batch(sl, hd, hd);
}

#define __DEF_WMSP_OBJ(objname, objsize, slab_size, ALIGN_SIZE) \
static char objname##_wmsp_poolmem[align_up(slab_size, HDEF_CACHE_LINE_BYTES)] ALIGN(ALIGN_SIZE) = {0};		\
static wmsp_t objname##_wmsp_pool = { .sptr = objname##_wmsp_poolmem, 						\
					.eptr = (void *)((paddr_t)objname##_wmsp_poolmem + slab_size)};		\
SAI wmobj_t * objname##_alloc_obj(){										\
	/*faa(&allocate_size, HDEF_CACHE_LINE_BYTES); printf("+%lu+\n", allocate_size); */			\
	return wmsp_alloc_head( &objname##_wmsp_pool, align_up(objsize, HDEF_CACHE_LINE_BYTES)); }		\
SAI void objname##_free_obj(wmobj_t *hd, wmobj_t *tl){								\
	/* word_t cnt = 1;											\
	wmobj_t *trav = hd;											\
	wmobj_t *next = (wmobj_t *)trav->node.next;								\
	while (next){ trav = next; next = (wmobj_t *)trav->node.next;  cnt++; }*/				\
	/*faa(&allocate_size, (0 - HDEF_CACHE_LINE_BYTES * cnt)); printf("-%lu-\n", allocate_size);*/		\
	wmsp_free_batch( &objname##_wmsp_pool, hd, tl); }\
SAI wmobj_t * objname##_palloc(size_t size){									\
	return wmsp_alloc_tail( &objname##_wmsp_pool, align_up(size, HDEF_CACHE_LINE_BYTES)); }

#define DEF_WMSP_OBJ(objname, objsize, slab_size) __DEF_WMSP_OBJ(objname, objsize, slab_size, PAGE_SIZE)
// #define DEF_WMSP_OBJ_TLSCACHE(objname, objsize, slab_size) \
// DEF_WMSP_OBJ(objname, objsize, slab_size)					   \
// SAI wmobj_t * objname##_cachealloc(wmobj_t *objname##_tlscache){ \
//	 wmobj_t *ret; \
//	 if (! objname##_tlscache ) { \
//	 ret = objname##_alloc_obj();  \
//	 /*assert(ret);*/ /*if (!ret) UNREACHABLE();*/ \
//	 if (!ret) fatal("no space for pobj\n"); \
//	 /* ASSUME(ret != NULL); */\
//	 return ret;} \
//	 ret = objname##_tlscache; \
//	 objname##_tlscache = (wmobj_t *) objname##_tlscache->node.next;\
//	 return ret;}	\
// SAI void objname##_cachefree(wmobj_t *objname##_tlscache, wmobj_t *hd){ wmobj_t *t = objname##_tlscache; objname##_tlscache = hd; hd->node.next = (tsnode_t *)t;}															  \
// SAI void objname##_threadend(wmobj_t *objname##_tlscache, void *arg){ wmobj_t *hd = objname##_tlscache; \
// if (!hd) { /*printf("empty");*/ return;} \
// objname##_tlscache = NULL; wmobj_t *trav = hd; wmobj_t *next = (wmobj_t *)trav->node.next; \
// while (next){ trav = next; next = (wmobj_t *)trav->node.next; } \
// objname##_spfree(hd, trav); };

#define __DEF_WMSP_OBJ_CACHED(objname, pcachelist)			\
SAI wmobj_t * objname##_cachealloc(){					\
	wmobj_t *ret;							\
	if (! (*(pcachelist)) ) {						\
	ret = objname##_alloc_obj();					\
	/* ASSERT(ret); */						\
	return ret;}							\
	ret = *(pcachelist);						\
	*(pcachelist) =  (wmobj_t *)(*(pcachelist))->node.next;		\
	return ret; }							\
SAI void objname##_cachefree(wmobj_t *hd){				\
	wmobj_t *t = *(pcachelist);					\
	*(pcachelist) = hd;						\
	hd->node.next = (tsnode_t *)t;}					\
SAI void objname##_threadend(){						\
	wmobj_t *hd = *(pcachelist);					\
	if (!hd) { /*printf("empty");*/ return; } 			\
	*(pcachelist) = NULL; wmobj_t *trav = hd; wmobj_t *next = (wmobj_t *)trav->node.next;	\
	while (next){ trav = next; next = (wmobj_t *)trav->node.next; }	\
	objname##_free_obj(hd, trav); }

#define DEF_WMSP_OBJ_CACHED(objname, pcachelist, objsize, slab_size)	\
DEF_WMSP_OBJ(objname, objsize, slab_size)				\
__DEF_WMSP_OBJ_CACHED(objname, pcachelist)				\
SYSINIT_FUNC(objname){ }


#define DEF_WMSP_OBJ_CACHED_HUGEPAGE(objname, pcachelist, objsize, slab_size, huge_page_size, mk_hugepage)	\
__DEF_WMSP_OBJ(objname, objsize, slab_size, huge_page_size)				\
__DEF_WMSP_OBJ_CACHED(objname, pcachelist)						\
SYSINIT_FUNC(objname){ mk_hugepage(objname##_wmsp_poolmem, slab_size, 1); INFO("mark hugepage success\n"); }
